package com.mloeppky.setgame.client;

import static com.mloeppky.setgame.client.SetBoard.newSetBoard;
import static com.mloeppky.setgame.client.SetUtils.BACKGROUND_STYLE;
import static com.mloeppky.setgame.client.SetUtils.isSet;

import java.util.HashSet;
import java.util.Set;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;

/**
 * Manages a GWT version of the card game Set.
 * @author mloeppky
 * @see <a href="http://www.setgame.com/set/rules_set.htm">Rules</a>
 */
public class SetGame implements EntryPoint, FocusHandler {
	/**
	 * Where all of the {@link SetCards} are managed and displayed.
	 */
	private SetBoard board;
	
	/**
	 * The cards that have been selected by the user. Should never exceed 3.
	 */
	private Set<SetCard> selectedCards;
	
	/**
	 * Displays how many sets can be made from the {@link SetCard}s that are on
	 * the board.
	 */
	private Label numSetsLabel;
	
	/**
	 * Program start method. Equivalent to main method.
	 */
	@Override
	public void onModuleLoad() {
		//Initialize variables
		selectedCards = new HashSet<SetCard>();
		numSetsLabel = new Label();
		board = newSetBoard(3, 3, this); //Create a new 3x3 SetBoard with this object catching FocusEvents from the cards
		Button addRowButton = new Button("Add Row");
		addRowButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {//The button was clicked
				try {
					board.addRow();
				} catch (EmptyDeckException e) {//All 81 cards have been played
					gameOver();
				}
				updateNumSetsLabel();
			}
		});
		updateNumSetsLabel();
		
		//Add UI components to the page
		RootPanel root = RootPanel.get("SetGame");
		root.add(board);
		root.add(numSetsLabel);
		root.add(addRowButton);
		root.setStyleName(BACKGROUND_STYLE);
	}
	
	/**
	 * To be called when the game is over. Currently congratulates the user.
	 */
	private void gameOver() {
		Window.alert("Congrats! The game is over though!");
		//TODO: Improvements are needed here!
	}
	
	/**
	 * Recalculates the number of sets that can be made from the {@link SetCard}s
	 * on the board and updates the {@link SetGame#numSetsLabel}.
	 */
	private void updateNumSetsLabel() {
		numSetsLabel.getElement().setInnerHTML("<h1>Number of Sets: " + board.numSets() + "<span></span></h1>");
	}
	
	/**
	 * Called when a {@link SetCard} gets selected. Marks the selected card as
	 * selected. If there are 3 cards selected then it is determined if they
	 * are a set or not. If they are a set they are removed from the board. If
	 * they are not a set then they are just deselected.
	 */
	@Override
	public void onFocus(FocusEvent event) {//A card got clicked
		if (event.getSource() instanceof SetCard) {
			SetCard sc = (SetCard)event.getSource();
			sc.setFocus(false);
			if (selectedCards.contains(sc)) {
				sc.setSelected(false);
				selectedCards.remove(sc);
			} else {
				sc.setSelected(true);
				selectedCards.add(sc);
			}
		}
		Timer t = new Timer() {
			@Override
			public void run() {
				if (selectedCards.size() == 3) {//The user has selected 3 cards
					if (isSet(selectedCards)) {//The 3 cards are a set
						Window.alert("Nice that is a set!");
						board.remove(selectedCards);
					} else {//The 3 cards are not a set
						Window.alert("Nope, that is not a set!");
						for (SetCard sc : selectedCards) {
							sc.setSelected(false);
						}
					}
					selectedCards.clear();
					updateNumSetsLabel();
				}
			}
		};
		t.schedule(10);
	}
	

	
}
